package ar.com.seminario.common.persistence.dao;

import java.io.Serializable;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

import ar.com.seminario.common.domain.entity.Identifiable;
import ar.com.seminario.common.persistence.exception.DaoException;


/**
 * Factory que instancia DAOs. Necesita un SessionFactory de Hibernate para obtener sesiones a
 * pasarle a los DAOs creados. Dicho SessionFactory se arma a partir del archivo que corresponda
 * (según el espacio de trabajo en el cual desea usarse el/los DAOs que se instancien).
 * 
 * @author Barbalarga & Camarro & Costa Zini & Lamas & Lamas
 */
public abstract class DaoFactory {

	/**
	 * Fábrica de sesiones de Hibernate.
	 */
	private SessionFactory sessionFactory;

	/**
	 * Obtiene el session factory utilizado para los DAOs.
	 * 
	 * @return Session factory.
	 */
	public final SessionFactory getSessionFactory() {

		return (this.sessionFactory);
	}

	/**
	 * Asigna el session factory a usar con los nuevos DAOs.
	 * 
	 * @param sessionFactory Session factory.
	 */
	public final void setSessionFactory(final SessionFactory sessionFactory) {

		this.sessionFactory = sessionFactory;
	}

	/**
	 * Instancia un nuevo GenericDao.
	 * 
	 * @param <E> Tipo de instancia que el DAO manejar.
	 * @param <Id> Tipo de identificador de E.
	 * @return DAO instanciado.
	 */
	public final <Id extends Serializable, E extends Identifiable<Id>> GenericDao<E, Id> newDao() {

		final GenericDao<E, Id> dao = new GenericDao<E, Id>();
		dao.setSession(this.getSession(this.sessionFactory));

		return (dao);
	}

	/**
	 * Instancia un nuevo tipo de DAO D, que debe ser una subclase de GenericDao.
	 * 
	 * @param <D> Tipo del DAO a retornar.
	 * @param <E> Tipo de la instancia del DAO a retornar.
	 * @param <Id> Tipo de identificador de E.
	 * @param type Tipo de clase a manejar.
	 * @return DAO instanciado.
	 * @throws DaoException En caso de problemas generando la instanciación.
	 */
	public final <Id extends Serializable, E extends Identifiable<Id>, D extends GenericDao<E, Id>>
	        D newDao(final Class<D> type) throws DaoException {

		try {
			final D dao = type.newInstance();
			dao.setSession(this.getSession(this.sessionFactory));

			return (dao);

		} catch (final InstantiationException e) {
			throw new DaoException(e);
		} catch (final IllegalAccessException e) {
			throw new DaoException(e);
		}
	}

	/**
	 * Obtiene la sesión de Hibernate que debe utilizar la implementación del DaoFactory que
	 * corresponda.
	 * 
	 * @param mySessionFactory Session Factory a utilizar para obtener la sesión.
	 * @return Sesión de Hibernate obtenida.
	 */
	protected abstract Session getSession(final SessionFactory mySessionFactory);
}
